package com.iotechn.unimall.app.api.leaseOrder;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.iotechn.unimall.app.config.ezviz.EzvizService;
import com.iotechn.unimall.biz.service.user.UserBizService;
import com.iotechn.unimall.core.exception.AppServiceException;
import com.iotechn.unimall.core.exception.ExceptionDefinition;
import com.iotechn.unimall.core.exception.ServiceException;
import com.iotechn.unimall.core.exception.ThirdPartServiceException;
import com.iotechn.unimall.core.util.GeneratorUtil;
import com.iotechn.unimall.data.component.CacheComponent;
import com.iotechn.unimall.data.component.LockComponent;
import com.iotechn.unimall.data.domain.*;
import com.iotechn.unimall.data.enums.OrderStatusType;
import com.iotechn.unimall.data.enums.UserLoginType;
import com.iotechn.unimall.data.mapper.*;
import com.iotechn.unimall.data.model.Page;
import com.iotechn.unimall.data.util.SessionUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;


@Service
public class LeaseOrderServiceImpl implements LeaseOrderService {

    private static final Logger logger = LoggerFactory.getLogger(LeaseOrderServiceImpl.class);

    private static final String TAKE_ORDER_LOCK = "TAKE_LEASE_ORDER_";

    @Value("${com.iotechn.unimall.machine-no}")
    private String MACHINE_NO;
    @Value("${com.iotechn.unimall.env}")
    private String ENV;
    @Value("${com.iotechn.unimall.wx.mini.app-id}")
    private String wxMiNiAppid;
    @Value("${com.iotechn.unimall.wx.app.app-id}")
    private String wxAppAppid;
    @Value("${com.iotechn.unimall.wx.h5.app-id}")
    private String wxH5Appid;

    @Autowired
    private LeaseOrderMapper leaseOrderMapper;
    @Autowired
    private LeaseDetailsMapper leaseDetailsMapper;
    @Autowired
    private LeaseOrderDetailsMapper leaseOrderDetailsMapper;
    @Autowired
    private AddressMapper addressMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private LockComponent lockComponent;
    @Autowired
    private WxPayService wxPayService;
    @Autowired
    private UserBizService userBizService;
    @Autowired
    private CacheComponent cacheComponent;
    @Autowired
    private EzvizService ezvizService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String takeOrder(LeaseOrderDO LeaseOrderDO, String channel, Long userId) throws ServiceException {
        //加上乐观锁，防止用户重复提交订单
        if (lockComponent.tryLock(TAKE_ORDER_LOCK + userId, 20)) {
            try {
                if (LeaseOrderDO.getTotalPrice() <= 0)
                    throw new AppServiceException(ExceptionDefinition.ORDER_PRICE_MUST_GT_ZERO);
                if(CollectionUtils.isEmpty(LeaseOrderDO.getDetail()))
                    throw new AppServiceException(ExceptionDefinition.PARAM_CHECK_FAILED);
                if (LeaseOrderDO.getAddressId() == null)
                    throw new AppServiceException(ExceptionDefinition.ORDER_ADDRESS_CANNOT_BE_NULL);

                List<LeaseOrderDetailsDO> orderList = LeaseOrderDO.getDetail();
                List<LeaseDetailsDO> landList = new ArrayList<>(orderList.size());
                Integer totalPrice = 0;
                for (LeaseOrderDetailsDO orderDetailsDO : orderList) {
                    LeaseDetailsDO leaseDetails = leaseDetailsMapper.selectById(orderDetailsDO.getLandId());
                    if(leaseDetails == null)
                        throw new AppServiceException(ExceptionDefinition.LEASE_LAND_NOT_EXIST);
                    if(leaseDetails.getIsLease() == 1)
                        throw new AppServiceException(ExceptionDefinition.LEASE_LAND_IS_LEASED);

                    totalPrice += leaseDetails.getPrice();
                    landList.add(leaseDetails);
                }
                if(!totalPrice.equals(LeaseOrderDO.getTotalPrice()))
                    throw new AppServiceException(ExceptionDefinition.ORDER_PRICE_CHECK_FAILED);

                Date now = new Date();
                LeaseOrderDO orderDo = new LeaseOrderDO();
                // 获取订单号
                String orderNo = GeneratorUtil.genOrderId(MACHINE_NO, ENV);
                orderDo.setChannel(channel);
                orderDo.setActualPrice(LeaseOrderDO.getTotalPrice());
                orderDo.setOrderNo(orderNo);
                orderDo.setUserId(userId);
                orderDo.setStatus(OrderStatusType.UNPAY.getCode());
                orderDo.setGmtUpdate(now);
                orderDo.setGmtCreate(now);

                // set address
                if (LeaseOrderDO.getAddressId() != null) {
                    AddressDO addressDO = addressMapper.selectById(LeaseOrderDO.getAddressId());
                    if (!userId.equals(addressDO.getUserId()))
                        throw new AppServiceException(ExceptionDefinition.ORDER_ADDRESS_NOT_BELONGS_TO_YOU);
                    orderDo.setConsignee(addressDO.getConsignee());
                    orderDo.setPhone(addressDO.getPhone());
                    orderDo.setProvince(addressDO.getProvince());
                    orderDo.setCity(addressDO.getCity());
                    orderDo.setCounty(addressDO.getCounty());
                    orderDo.setAddress(addressDO.getAddress());
                }
                leaseOrderMapper.insert(orderDo);

                // 插入到详情表
                {
                    for (LeaseDetailsDO land : landList) {
                        LeaseOrderDetailsDO detailsDO = new LeaseOrderDetailsDO();
                        detailsDO.setLandNo(land.getLandNo());
                        detailsDO.setLandId(land.getId());
                        detailsDO.setImageUrl(land.getImageUrl());
                        detailsDO.setNum(land.getNums());
                        detailsDO.setLeaseTitle(LeaseOrderDO.getTitle());
                        detailsDO.setPrice(land.getPrice());
                        detailsDO.setArea(land.getArea());
                        detailsDO.setOrderNo(orderNo);
                        detailsDO.setOrderId(orderDo.getId());
                        detailsDO.setLandNo(land.getLandNo());
                        detailsDO.setLeaseId(land.getLeaseId());
                        leaseOrderDetailsMapper.insert(detailsDO);
                        // 减库存
                        Integer count = leaseDetailsMapper.updateStatus(land.getId(), 1, 0);
                        if(count <= 0)
                            throw new AppServiceException(ExceptionDefinition.LEASE_LAND_IS_LEASED);
                    }
                }

                return orderNo;
            } catch (ServiceException e) {
                throw e;
            } catch (Exception e) {
                logger.error("[提交订单] 异常", e);
                throw new AppServiceException(ExceptionDefinition.ORDER_UNKNOWN_EXCEPTION);
            } finally {
                lockComponent.release(TAKE_ORDER_LOCK + userId);
            }
        }
        throw new AppServiceException(ExceptionDefinition.ORDER_SYSTEM_BUSY);
    }

    @Override
    public String form(String title, String landIds, Long userId) throws AppServiceException {

        String[] ss = landIds.split(",");
        List<String> list = new ArrayList<>();
        Collections.addAll(list,ss);
        List<LeaseDetailsDO> leaseDetailsDOS = leaseDetailsMapper.selectBatchIds(list);

        if(CollectionUtils.isEmpty(leaseDetailsDOS) || leaseDetailsDOS.size() != ss.length)
            throw new AppServiceException(ExceptionDefinition.LEASE_LAND_NOT_EXIST);
        int totalPrice = 0;
        for (LeaseDetailsDO land : leaseDetailsDOS) {
            if(land.getIsLease() == 1)
                throw new AppServiceException(ExceptionDefinition.LEASE_LAND_IS_LEASED);
            totalPrice += land.getPrice();
        }

        JSONObject json = new JSONObject();
        UserDO userDO = userMapper.selectList(new EntityWrapper<UserDO>().setSqlSelect("nickname","phone").eq("id",userId)).get(0);
        json.put("name",userDO.getNickname());
        json.put("phone",userDO.getPhone());
        json.put("title",title);
        json.put("details",leaseDetailsDOS);
        json.put("price",totalPrice);
        json.put("leaseId",leaseDetailsDOS.get(0).getLeaseId());

        return json.toJSONString();
    }


    @Override
    public Page<LeaseOrderDO> list(Integer page, Integer limit, Long userId) throws ServiceException {
        List<LeaseOrderDO> leaseOrderDOS = leaseOrderMapper.selectFrontListById(userId,(page - 1) * limit, limit);
        return new Page<LeaseOrderDO>(leaseOrderDOS, page, limit, leaseOrderDOS.size());
    }

    @Override
    public LeaseOrderDO detail(String orderNo, Long userId) throws ServiceException {
        LeaseOrderDO leaseOrderDO = leaseOrderMapper.selectDetailByNo(orderNo);
        String accessToken = cacheComponent.getRaw(EzvizService.ACCESS_TOKEN);
        if(StringUtils.isBlank(accessToken)) {
            synchronized (EzvizService.ACCESS_TOKEN){
                if(StringUtils.isBlank(accessToken))
                    ezvizService.refresh();
            }
            accessToken = cacheComponent.getRaw(EzvizService.ACCESS_TOKEN);
        }
        leaseOrderDO.getLease().setAccessToken(accessToken);
        return leaseOrderDO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object wxPrepay(String orderNo, String ip, Long userId) throws ServiceException {
        Date now = new Date();
        Wrapper<LeaseOrderDO> wrapper = new EntityWrapper<LeaseOrderDO>()
                .eq("order_no", orderNo).eq("user_id", userId);
        List<LeaseOrderDO> orderDOS = leaseOrderMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(orderDOS))
            throw new AppServiceException(ExceptionDefinition.ORDER_NOT_EXIST);
        LeaseOrderDO orderDO = orderDOS.get(0);
        // 检测订单状态
        if (orderDO.getStatus() != OrderStatusType.UNPAY.getCode()) {
            throw new AppServiceException(ExceptionDefinition.ORDER_STATUS_NOT_SUPPORT_PAY);
        }

        Integer loginType = SessionUtil.getUser().getLoginType();
        String appId;
        String tradeType;
        if (UserLoginType.MP_WEIXIN.getCode() == loginType) {
            appId = wxMiNiAppid;
            tradeType = WxPayConstants.TradeType.JSAPI;
        } else if (UserLoginType.APP_WEIXIN.getCode() == loginType || UserLoginType.REGISTER.getCode() == loginType) {
            appId = wxAppAppid;
            tradeType = WxPayConstants.TradeType.APP;
        } else if (UserLoginType.H5_WEIXIN.getCode() == loginType) {
            appId = wxH5Appid;
            tradeType = WxPayConstants.TradeType.JSAPI;
        } else {
            throw new AppServiceException(ExceptionDefinition.ORDER_LOGIN_TYPE_NOT_SUPPORT_WXPAY);
        }

        Object result ;
        try {
            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            orderRequest.setAppid(appId);
            orderRequest.setOutTradeNo(orderNo);
            orderRequest.setOpenid(SessionUtil.getUser().getOpenId());
            orderRequest.setBody("订单：" + orderNo);
            orderRequest.setTotalFee(orderDO.getActualPrice());
            orderRequest.setSpbillCreateIp(ip);
            orderRequest.setTradeType(tradeType);
            result = wxPayService.createOrder(orderRequest);
            if (result instanceof WxPayMpOrderResult) {
                String prepayId = ((WxPayMpOrderResult)result).getPackageValue();
                prepayId = prepayId.replace("prepay_id=", "");
                UserFormIdDO userFormIdDO = new UserFormIdDO();
                userFormIdDO.setFormId(prepayId);
                userFormIdDO.setUserId(userId);
                userFormIdDO.setOpenid(SessionUtil.getUser().getOpenId());
                userFormIdDO.setGmtUpdate(now);
                userFormIdDO.setGmtCreate(now);
                userBizService.setValidFormId(userFormIdDO);
            }
        } catch (WxPayException e) {
            logger.error("[微信支付] 异常", e);
            throw new ThirdPartServiceException(e.getErrCodeDes(), ExceptionDefinition.THIRD_PART_SERVICE_EXCEPTION.getCode());
        } catch (Exception e) {
            logger.error("[预付款异常]", e);
            throw new AppServiceException(ExceptionDefinition.ORDER_UNKNOWN_EXCEPTION);
        }
        return result;
    }

}
